package com.mybatis.boost.extend.exception;

import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.apache.ibatis.ognl.OgnlException;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.dao.DataAccessException;

import javax.annotation.Nullable;
import javax.sql.DataSource;

/**
 * Translates MyBatis SqlSession returned exception into a Spring
 * {@code DataAccessException} using Spring's {@code SQLExceptionTranslator}
 * Can load {@code SQLExceptionTranslator} eagerly of when the
 * first exception is translated.
 *
 * @author jearton
 * @since 2017/2/14
 */
public class MyBatisExceptionTranslator extends org.mybatis.spring.MyBatisExceptionTranslator {

    /**
     * Creates a new {@code DataAccessExceptionTranslator} instance.
     *
     * @param dataSource                  DataSource to use to find metadata and establish which error codes are usable.
     * @param exceptionTranslatorLazyInit if true, the translator instantiates internal stuff only the first time will
     */
    public MyBatisExceptionTranslator(DataSource dataSource, boolean exceptionTranslatorLazyInit) {
        super(dataSource, exceptionTranslatorLazyInit);
    }

    @Nullable
    @Override
    public DataAccessException translateExceptionIfPossible(RuntimeException e) {
        DataAccessException exception = super.translateExceptionIfPossible(e);
        if (exception == null) {
            return null;
        }
        if (exception instanceof MyBatisSystemException) {
            return _customTranslateException((MyBatisSystemException) exception);
        }
        return exception;
    }

    private DataAccessException _customTranslateException(MyBatisSystemException e) {
        PersistenceException unwrap = (PersistenceException) e.getCause();
        if (unwrap.getCause() instanceof OgnlException) {
            _process((OgnlException) unwrap.getCause());
        }
        _process(unwrap);
        return e;
    }

    private void _process(OgnlException e) {
        if (e.getCause() instanceof IllegalArgumentException) {
            throw (IllegalArgumentException) e.getCause();
        }
    }

    private void _process(PersistenceException e) {
        if (e.getCause() instanceof IllegalArgumentException) {
            throw (IllegalArgumentException) e.getCause();
        }
        if (e.getCause() instanceof NoResultException) {
            throw (NoResultException) e.getCause();
        }
        if (e.getCause() instanceof TooManyResultsException) {
            throw (TooManyResultsException) e.getCause();
        }
        if (e.getCause() instanceof NoGeneratorKeysException) {
            throw (NoGeneratorKeysException) e.getCause();
        }
    }
}
